Skip to content

Optimize Android emulator initialization by batching reboots#5280

Open
jardondiego wants to merge 10 commits into
masterfrom
optimize-android-reboots
Open

Optimize Android emulator initialization by batching reboots#5280
jardondiego wants to merge 10 commits into
masterfrom
optimize-android-reboots

Conversation

@jardondiego
Copy link
Copy Markdown
Collaborator

This change reduces the number of reboots during Android device setup by:

  • Adding a wait_for_reboot parameter to adb.write_data_to_file.
  • Tracking reboot status in initialize_device to skip the final reboot if one already occurred.
  • Disabling reboots when setting sanitizer options since the app restart is sufficient.

These optimizations improve bot startup efficiency and overall fuzzing throughput.

This change reduces the number of reboots during Android device setup by:
- Adding a wait_for_reboot parameter to adb.write_data_to_file.
- Tracking reboot status in initialize_device to skip the final reboot if one already occurred.
- Disabling reboots when setting sanitizer options since the app restart is sufficient.

These optimizations improve bot startup efficiency and overall fuzzing throughput.
@jardondiego jardondiego marked this pull request as ready for review May 17, 2026 00:44
@jardondiego jardondiego requested a review from a team as a code owner May 17, 2026 00:44
Copy link
Copy Markdown
Collaborator

@IvanBM18 IvanBM18 May 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:
Add typing hints and update the string doc for this method so that other devs reading this know what the bool means

return True


def get_debug_props_and_values():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit x2:
Add typing hints and update the string doc for this method so that other devs reading this know what the bool means

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with Ivan

sanitizer_options, sanitizer_options_file_path, wait_for_reboot=False)


def setup_asan_if_needed():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit x3:
Add typing hints and update the string doc for this method so that other devs reading this know what the bool means

Copy link
Copy Markdown
Collaborator

@IvanBM18 IvanBM18 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, minus some NITs

@jardondiego jardondiego requested a review from letitz May 18, 2026 19:27
Copy link
Copy Markdown
Collaborator

@fernandofloresg fernandofloresg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, would be nice to add those docstrings

return True


def get_debug_props_and_values():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with Ivan

Adds `-> bool` return type hints and explanatory docstrings to:
- `configure_system_build_properties`
- `setup_asan_if_needed`

These updates clarify that the returned boolean indicates whether a device reboot occurred during the setup step.
Copy link
Copy Markdown
Collaborator

@letitz letitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused about the main logic change in initialize_device(), see comment below.



def write_data_to_file(contents, file_path):
def write_data_to_file(contents, file_path, wait_for_reboot=True):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should document the new argument at least. While we're at it, we can document the whole function: args, return value, any exceptions it raises.

current_md5 = adb.get_file_checksum(BUILD_PROP_PATH)
persistent_cache.set_value(constants.BUILD_PROP_MD5_KEY, current_md5)

return True
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading this right, we only rebooted on line 239 if the android version was as least M?


# General device configuration settings.
configure_system_build_properties()
needs_full_reboot = configure_system_build_properties()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring above states that this function returns whether the device was already rebooted, not whether it needed to be rebooted. Which is it?

# Reboot device as above steps would need it and also it brings device in a
# good state.
reboot()
if needs_full_reboot or not asan_reboot_done:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a hard time understanding the logic here, regardless of whether this boolean really means needs_full_reboot or if it means already_rebooted, per the above comment.

In the needs_full_reboot case: if we needed to reboot after configuring the device, and the asan setup step already rebooted it, then why do we reboot once more here? In other words, why is this not:

Suggested change
if needs_full_reboot or not asan_reboot_done:
if needs_full_reboot and not asan_reboot_done:

In the already_rebooted case: why do avoid rebooting if already_rebooted is true? I would expect instead:

Suggested change
if needs_full_reboot or not asan_reboot_done:
if not already_rebooted and not asan_reboot_done:

return

adb.write_data_to_file(sanitizer_options, sanitizer_options_file_path)
# Skip reboot as the app will pick up the options file on restart.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure I understand, this means that the next time the app is started, it will read from the options file?


def setUp(self):
# Mock environment to bypass the engine fuzzer check
mock.patch(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the first time I see mocks written like this in ClusterFuzz. Historically I've seen use of helpers.patch 1 and mock.patch decorators 2. In this case, I would expect the former.

self.mock_setup_asan.return_value = False

device.initialize_device()
self.mock_reboot.assert_called_once()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We test False/True, True/False and False/False. What about True/True? This goes back to my comment about or not vs and not.

mock.patch.stopall()

def test_reboot_if_props_changed(self):
"""Test that it reboots if build.prop changed, even if ASan didn't run."""
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Test that it reboots if build.prop changed, even if ASan didn't run."""
"""Test that `initialize_device()` reboots the device if
`configure_system_build_properties()` did, and the ASan setup script did not.

self.mock_reboot.assert_called_once()

def test_no_reboot_if_asan_ran(self):
"""Test that the final reboot is skipped if ASan setup performed a shell restart."""
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Test that the final reboot is skipped if ASan setup performed a shell restart."""
"""Test that `initialize_device()` skips calling `reboot()` if
`configure_system_build_properties()` did not cause a reboot but the ASan
setup script did.
"""

self.mock_reboot.assert_not_called()

def test_reboot_if_clean_slate_needed(self):
"""Test that it still reboots to ensure a clean state if no other steps restarted it."""
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Test that it still reboots to ensure a clean state if no other steps restarted it."""
"""Test that `initialize_device()` calls `reboot()` if neither
`configure_system_build_properties()` nor the ASan setup script did.
"""

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants